/* Sentinel - IRC Statistical and Operator Services
** connection.c - Socket struct manipulation routines
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: connection.c 7 2010-02-01 19:17:42Z dubkat $
*/

#include "stats.h"
#include "struct.h"
#include "dlink.h"
#include "proto.h"
#include "slog.h"
#include "mem.h"

dlink_list connection_list;
dlink_list dying_list;

struct Connection *add_connection(int sock, char *name, unsigned int type)
{
  struct Connection *c = smalloc(sizeof(struct Connection));
  dlink_node *dl = dlink_create();

  c->fd = sock;
  c->flags |= type;
  strlcpy(c->name, name, LINE);
  dlink_add(c, dl, &connection_list);
  memcounts.connection++;

  return c;
}

void dying_connection(int sock)
{
  struct Connection *c;
  dlink_node *dl;
  dlink_node *tdl;

  DLINK_FOREACH(dl, connection_list.head)
  {
    c = (struct Connection *) dl->data;
    if (c->fd == sock)
    {
      /* Mark this connection as no longer online in
      ** case it was logged in
      */
      if (c->myuser != NULL)
      {
        c->myuser->flags &= ~MFLAG_DCC;
        /* If the myuser struct is marked as a dying
        ** one, and they aren't logged in via msg,
        ** remove it.
        */
        if (IsOperDying(c->myuser) &&
            !IsOperOn(c->myuser))
        {
          tdl = dlink_find_delete(c->myuser, &myudying);
          dlink_free(tdl);
#ifdef HAVE_LIBCRYPTO

          if (c->myuser->response != NULL)
            free(c->myuser->response);
          if (c->myuser->keyfile != NULL)
            RSA_free(c->myuser->keyfile);
#endif

          free(c->myuser);
          memcounts.myuser--;
        }
      }
      dlink_delete(dl, &connection_list);
      dlink_add(c, dl, &dying_list);
      return ;
    }
  }

  slog(DEFAULT_LOG, L_ERR, "Unable to move connection struct to dying:  fd %d", sock);
}

void delete_connection(int sock)
{
  struct Connection *c;
  dlink_node *dl;
  dlink_node *tdl;
  struct sendq *sq;

  DLINK_FOREACH(dl, connection_list.head)
  {
    c = (struct Connection *) dl->data;
    if (c->fd == sock)
    {
      /* Mark this connection as no longer online in
      ** case it was logged in
      */
      if (c->myuser != NULL)
      {
        c->myuser->flags &= ~MFLAG_DCC;
        /* If the myuser struct is marked as a dying
        ** one, and they aren't logged in via msg,
        ** remove it.
        */
        if (IsOperDying(c->myuser) &&
            !IsOperOn(c->myuser))
        {
          tdl = dlink_find_delete(c->myuser, &myudying);
          dlink_free(tdl);
#ifdef HAVE_LIBCRYPTO

          if (c->myuser->response != NULL)
            free(c->myuser->response);
          if (c->myuser->keyfile != NULL)
            RSA_free(c->myuser->keyfile);
#endif

          free(c->myuser);
          memcounts.myuser--;
        }
      }
      dlink_delete(dl, &connection_list);
      dlink_free(dl);

      DLINK_FOREACH_SAFE(dl, tdl, c->sendq.head)
      {
        sq = dl->data;

        free(sq->buf);
        free(sq);
        /* Since we're freeing the connection shortly after, no need to remove
        ** it from the list.
        */
        dlink_free(dl);
      }

      if (c->sa != NULL)
        free(c->sa);
      free(c);
      memcounts.connection--;
      return ;
    }
  }

  slog(DEFAULT_LOG, L_ERR, "Unable to delete connection struct:  fd %d", sock);
}

struct Connection *find_connection(int sock)
{
  struct Connection *c;
  dlink_node *dl;

  DLINK_FOREACH(dl, connection_list.head)
  {
    c = (struct Connection *) dl->data;
    if (c->fd == sock)
      return c;
  }
  return NULL;
}
